#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include "ai_test.h"
#include "pcie_mem.h"

enum ai_bar0_offset_t {
    AI_CMD_BAR0_OFFSET = 0x08,          /* AI 命令寄存器偏移 */
    AI_ID_DATA_BAR0_OFFSET = 0x38,      /* AI ID 数据寄存器偏移 */
    AI_SET_MODE_BAR0_OFFSET = 0x48,     /* AI 设置模式寄存器偏移 */
};

#define AI_START_CMD        0x1111      /* 启动 AI 命令 */
#define AI_STOP_CMD         0x2222      /* 停止 AI 命令 */
#define AI_CNOFIG_CMD       0x3333      /* 配置 AI 命令 */

#define AI_DATA_SHIFT       0           /* AI 数据左移位数 */
#define AI_ID_SHIFT         24          /* AI ID 左移位数 */

#define ADC_10V_RANGE       0x00
#define ADC_2V5_RANGE       0x01
#define ADC_5V_RANGE        0X02

#define ADC_10V_RANGE_LSB   305         /* 305uV */
#define ADC_2V5_RANGE_LSB   76          /* 76uV */
#define ADC_5V_RANGE_LSB    152         /* 152uV */

#define AI_TSAS_SHIFT       0           /* AI TSAS 左移位数 */
#define AI_MODE_SHIFT       19          /* AI 模式左移位数 */

#define AI_DEFAULT_MODE     0x03
#define AI_DEFAULT_TSAS     10           /* us */

struct ai_info_t {
    uint8_t channel;                    /* 通道号 */
    uint8_t channel_min;                /* 最小通道号 */
    uint8_t channel_max;                /* 最大通道号 */
    double value;                       /* AI 输入值 */
};

static void set_ai_mode(struct pcie_mem_ctx *ctx, uint32_t value)
{
    volatile uint32_t *addr;

    if (!ctx->map_base) {
        printf("错误: 内存未映射!\n");
        return;
    }

    addr = (volatile uint32_t *)((uintptr_t)ctx->map_base);
    *(volatile uint32_t *)((uintptr_t)addr + AI_SET_MODE_BAR0_OFFSET) = value;

    printf("写入: reg=0x%04X, val=0x%08X\n",
            AI_SET_MODE_BAR0_OFFSET, value);
}

static void set_ai_cmd(struct pcie_mem_ctx *ctx, uint32_t value)
{
    volatile uint32_t *addr;

    if (!ctx->map_base) {
        printf("错误: 内存未映射!\n");
        return;
    }

    addr = (volatile uint32_t *)((uintptr_t)ctx->map_base);
    *(volatile uint32_t *)((uintptr_t)addr + AI_CMD_BAR0_OFFSET) = value;

    printf("写入: reg=0x%04X, val=0x%08X\n",
            AI_CMD_BAR0_OFFSET, value);
}

static uint32_t get_single_ai(struct pcie_mem_ctx *ctx, uint8_t channel)
{
    volatile uint32_t *addr;
    uint32_t value;

    if (!ctx->map_base) {
        printf("错误: 内存未映射!\n");
        return 0;
    }

    addr = (volatile uint32_t *)((uintptr_t)ctx->map_base);
    *(volatile uint32_t *)((uintptr_t)addr + AI_ID_DATA_BAR0_OFFSET) = (uint32_t)(channel << AI_ID_SHIFT);
    /* 注意：这里需要将字节偏移转换为 uint32_t 偏移 */
    value = *(addr + (AI_ID_DATA_BAR0_OFFSET / sizeof(uint32_t)));

    printf("写入: reg=0x%04X, val=0x%08X\n",
            AI_ID_DATA_BAR0_OFFSET, (uint32_t)(channel << AI_ID_SHIFT));
    printf("读取: reg=0x%04X, val=0x%08X\n",
            AI_ID_DATA_BAR0_OFFSET, value);

    return (value & 0xFFFFFF);
}

uint32_t get_single_ai_test(struct pcie_mem_ctx *ctx, struct ai_info_t ai_info)
{
    uint32_t reg_val = 0;

    if ((ai_info.channel >= ai_info.channel_min) && (ai_info.channel <= ai_info.channel_max)) {
        reg_val = get_single_ai(ctx, ai_info.channel);
        ai_info.value = reg_val * ADC_10V_RANGE_LSB / 1000000.0;
        printf("读取通道 %d 的 AI 输入值为 %.6lfV\n", ai_info.channel, ai_info.value);
    } else {
        printf("参数错误: %d ≤ channel=%d ≤ %d\n",
               ai_info.channel_min, ai_info.channel, ai_info.channel_max);
        reg_val = 0;
    }

    return reg_val;
}

static int usage(int argc, char *argv[])
{
    if ((argc < 2) || (argc > 3)) {
        printf("用法: %s %s channel\n", argv[0], argv[1]);
        printf("示例: %s %s 0\n", argv[0], argv[1]);
        printf("用法: %s %s n", argv[0], argv[1]);
        printf("示例: %s %s \n", argv[0], argv[1]);
        return EXIT_FAILURE;
    } else {
        return EXIT_SUCCESS;
    }
}

int ai_test(int argc, char *argv[])
{
    int ret = 0;
    uint8_t i = 0;
    const char *device_address = PCIE_AI_DEVICE_ADDRESS;
    uint32_t base_addr = PCIE_AI_BASE_ADDR_BAR0;
    size_t map_size = PCIE_AI_BAR0_MAP_SIZE;
    struct pcie_mem_ctx ctx = {
        .fd = -1,
        .map_base = NULL,
        .phys_base = 0,
        .map_size = 0
    };
    struct ai_info_t ai_info = {
        .channel = 0,
        .channel_min = 0,
        .channel_max = 15,
        .value = 0,
    };

    if (EXIT_SUCCESS != usage(argc, argv)) {
        return EXIT_FAILURE;
    }

    if (argc == 3) {
        ai_info.channel = atoi(argv[2]);
    } else if (argc == 2) {
        ai_info.channel = ai_info.channel_max + 1;
    }

    printf("正在获取通道 %d 的 AI...\n",
            ai_info.channel);

    if (true == is_pcie_device_enabled(device_address)) {
        printf("设备已启用\n");
    } else {
        printf("设备未启用\n");
        if (enable_pcie_device(device_address) == 0) {
            printf("设备启用成功\n");
        } else {
            printf("设备启用失败!\n");
            return EXIT_FAILURE;
        }
    }

    ret = init_pcie_mem(&ctx, base_addr, map_size);
    if (ret < 0) {
        printf("初始化内存映射失败!\n");
        return EXIT_FAILURE;
    }

    set_ai_mode(&ctx, (AI_DEFAULT_MODE << AI_MODE_SHIFT | AI_DEFAULT_TSAS << AI_TSAS_SHIFT));
    set_ai_cmd(&ctx, AI_CNOFIG_CMD);
    usleep(100000);
    set_ai_cmd(&ctx, AI_START_CMD);

    if (ai_info.channel == (ai_info.channel_max + 1)) {
        for (i = ai_info.channel_min; i <= ai_info.channel_max; i++) {
            ai_info.channel = i;
            get_single_ai_test(&ctx, ai_info);
        }
    } else {
        get_single_ai_test(&ctx, ai_info);
    }

    cleanup_pcie_mem(&ctx);

    printf("通道 %d 的测试已完成\n", ai_info.channel);

    return EXIT_SUCCESS;
}
